package com.lqm.mall.product.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lqm.common.constant.RedisConstant;
import com.lqm.common.utils.PageUtils;
import com.lqm.common.utils.Query;
import com.lqm.mall.product.dao.CategoryBrandRelationDao;
import com.lqm.mall.product.dao.CategoryDao;
import com.lqm.mall.product.entity.CategoryEntity;
import com.lqm.mall.product.service.CategoryService;
import com.lqm.mall.product.vo.Catelog2Vo;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;


@Slf4j
@Service("categoryService")
public class CategoryServiceImpl extends ServiceImpl<CategoryDao, CategoryEntity> implements CategoryService {

    @Autowired
    private CategoryBrandRelationDao categoryBrandRelationDao;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private RedissonClient redissonClient;

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<CategoryEntity> page = this.page(
                new Query<CategoryEntity>().getPage(params),
                new QueryWrapper<CategoryEntity>()
        );

        return new PageUtils(page);
    }

    @Override
    public List<CategoryEntity> listWithTree() {
        //1、查出所有分类
        List<CategoryEntity> entities = baseMapper.selectList(null);
        //2、组装父子结构的树型结构
        List<CategoryEntity> level0 = entities.stream().filter((categoryEntity -> {
            return categoryEntity.getParentCid() == 0;
        })).map((menu)->{
            menu.setChildren(getChildren(menu,entities));
            return menu;
        }).collect(Collectors.toList());
        sort(level0);
        return level0;
    }

    @Override
    public void removeMenuByIds(List<Long> asList) {
        //TODO  1、检查当前删除的菜单，是否被别的地方引用

        //逻辑删除
        baseMapper.deleteBatchIds(asList);
    }

    @Transactional
    @Override
    public void updateCascade(CategoryEntity category) {
        this.updateById(category);
        //TODO 级联删除
        categoryBrandRelationDao.updateCategory(category.getCatId(),category.getName());
    }

    @Override
    public Long[] findCategoryPath(Long catelogId) {
        List<Long> path=new ArrayList<>();
        path.add(catelogId);
        CategoryEntity categoryEntity = this.baseMapper.selectById(catelogId);
        while (categoryEntity!=null){
            if(categoryEntity.getParentCid()!=null && categoryEntity.getParentCid()!=0){
                path.add(categoryEntity.getParentCid());
            }
            categoryEntity=this.baseMapper.selectById(categoryEntity.getParentCid());
        }
        Long[] res=new Long[path.size()];
        for(int i=0;i<path.size();++i){
            res[i]=path.get(path.size()-1-i);
        }
        return res;
    }

    @Override
    public List<CategoryEntity> getLevel1Categorys() {
        List<CategoryEntity> entities = this.baseMapper.selectList(new QueryWrapper<CategoryEntity>().eq("parent_cid", 0));
        return entities;
    }


    @Override
    public Map<String, List<Catelog2Vo>> getCatalogJSON(){
        Map<String, List<Catelog2Vo>> catalogJSON = null;
        String catalog=stringRedisTemplate.opsForValue().get(RedisConstant.PRODUCT_CATALOGJSON);
        if(!StringUtils.isEmpty(catalog)){
            catalogJSON = JSON.parseObject(catalog, new TypeReference<Map<String, List<Catelog2Vo>>>() {});
            return catalogJSON;
        }
        log.info("获取缓存失败--读取数据库");
        catalogJSON=getCatalogJSONFromDbWithRedisLock();
        return catalogJSON;
    }

    //TODO 此处使用lettecure时有堆外内存溢出
    //使用缓存进行优化 , 分布式锁保证缓存一致
    public Map<String, List<Catelog2Vo>> getCatalogJSONFromDbWithRedisLock() {
        RLock lock = redissonClient.getLock(RedisConstant.PRODUCT_CATALOGJSON_LOCK);
        Map<String, List<Catelog2Vo>> map = null;
        lock.lock();
        try{
            String catalogJSON=stringRedisTemplate.opsForValue().get(RedisConstant.PRODUCT_CATALOGJSON);
            if(!StringUtils.isEmpty(catalogJSON)){
                Map<String, List<Catelog2Vo>> catalog = JSON.parseObject(catalogJSON, new TypeReference<Map<String, List<Catelog2Vo>>>() {
                });
                return catalog;
            }
            log.info("读取数据库---");
            List<CategoryEntity> categoryEntityList=this.baseMapper.selectList(null);
            List<CategoryEntity> level1Categorys = getParentCid(categoryEntityList,0L);
            if(level1Categorys!=null){
                map=level1Categorys.stream().collect(Collectors.toMap(k -> {
                    return k.getCatId().toString();
                }, v -> {
                    List<Catelog2Vo> catelog2Vos = getParentCid(categoryEntityList,v.getCatId()).stream().map((item) -> {
                        List<CategoryEntity> entities = getParentCid(categoryEntityList, item.getCatId());
                        List<Catelog2Vo.Catalog3Vo> collect =null;
                        if(entities!=null){
                            collect = entities.stream().map((t) -> {
                                Catelog2Vo.Catalog3Vo catalog3Vo1 = new Catelog2Vo.Catalog3Vo(t.getCatId().toString(), t.getCatId().toString(), t.getName());
                                return catalog3Vo1;
                            }).collect(Collectors.toList());
                        }
                        Catelog2Vo vo = new Catelog2Vo(item.getCatId().toString(), collect, item.getCatId().toString(), item.getName());
                        return vo;
                    }).collect(Collectors.toList());
                    return catelog2Vos;
                }));
            }else{
                map=new HashMap<>();
            }
            String json = JSON.toJSONString(map);
            //保存到缓存中
            stringRedisTemplate.opsForValue().set(RedisConstant.PRODUCT_CATALOGJSON,json);
        }finally {
            lock.unlock();
        }
        return map;
    }

    private List<CategoryEntity> getParentCid(List<CategoryEntity> categoryEntityList, Long pId) {
        return categoryEntityList.stream().filter((item)->{
            return item.getParentCid()==pId;
        }).collect(Collectors.toList());
    }


    private List<CategoryEntity> getChildren(CategoryEntity categoryEntity,List<CategoryEntity> entities){
        List<CategoryEntity> childred=new ArrayList<>();
        for (CategoryEntity entity : entities) {
            if(entity.getParentCid()==categoryEntity.getCatId()){
                childred.add(entity);
                entity.setChildren(getChildren(entity,entities));
            }
        }
        if(!childred.isEmpty()){
            sort(childred);
        }
        return childred;
    }

    private void sort(List<CategoryEntity> childred) {
        if(childred==null){
            return;
        }
        childred.sort((child1,child2)->{
            int c1=0;
            int c2=0;
            if(child1!=null && child1.getSort()!=null){
                c1=child1.getSort();
            }
            if(child2!=null && child2.getSort()!=null){
                c2=child2.getSort();
            }
            return c1-c2;
        });
    }
}